import asyncio
from bleak import BleakScanner, BleakClient, BleakError
from PySide6.QtCore import QObject, Signal, Slot

class BLEManager(QObject):
    """
    管理BLE扫描、连接和数据交互的类.
    """
    device_found_signal = Signal(object, object) # 修改信号以传递完整的advertisement_data
    services_resolved_signal = Signal(object)
    connection_status_signal = Signal(str, str, str) # status, address, error_msg
    advertisement_updated_signal = Signal(str, object) # address, advertisement_data
    characteristic_read_signal = Signal(str, object) # uuid, value (bytearray)
    notification_received_signal = Signal(str, object) # uuid, value (bytearray)
    
    # 为协议测试添加
    protocol_data_updated_signal = Signal(str, object) # characteristic_uuid, value
    
    def __init__(self, parent=None):
        super().__init__(parent)
        self.scanner = BleakScanner(detection_callback=self.detection_callback)
        self.devices = {} # device.address -> {"device": BLEDevice, "adv_data": AdvertisementData}
        self.connected_client = None
        self.is_scanning = False
        self.notification_handlers = {}

    def detection_callback(self, device, advertisement_data):
        """
        BleakScanner发现设备时的回调函数.
        """
        # 过滤掉没有名字的设备
        if not device.name:
            return
            
        # 无论设备是否已发现, 都发出更新信号, 用于实时更新UI
        self.advertisement_updated_signal.emit(device.address, advertisement_data)

        if device.address not in self.devices:
            # 存储设备信息和最新的广播数据
            self.devices[device.address] = {"device": device, "adv_data": advertisement_data}
            # 发射信号，将新发现的设备和其广播数据传递给UI线程
            self.device_found_signal.emit(device, advertisement_data)
        else:
            # 如果设备已存在, 只更新其广播数据
            self.devices[device.address]["adv_data"] = advertisement_data


    async def start_scan(self):
        """
        开始连续扫描BLE设备.
        """
        self.devices.clear()
        await self.scanner.start()
        self.is_scanning = True

    async def stop_scan(self):
        """
        停止扫描.
        """
        await self.scanner.stop()
        self.is_scanning = False

    async def connect_to_device(self, address):
        """
        连接到指定的BLE设备.
        """
        self.connection_status_signal.emit("connecting", address, "")
        try:
            if self.connected_client and self.connected_client.is_connected:
                # 如果已经存在一个连接, 先断开它
                await self.connected_client.disconnect()

            client = BleakClient(address)
            await client.connect()
            self.connected_client = client
            self.connection_status_signal.emit("connected", address, "")
            
            # 连接成功后发现服务
            await self.discover_services()

        except Exception as e:
            self.connection_status_signal.emit("failed", address, str(e))
            print(f"连接失败: {e}")

    async def disconnect_device(self):
        """
        断开当前连接的设备.
        """
        if self.connected_client and self.connected_client.is_connected:
            address = self.connected_client.address
            await self.connected_client.disconnect()
            self.connected_client = None
            # 清理
            self.notification_handlers.clear()
            self.connection_status_signal.emit("disconnected", address, "")

    async def discover_services(self):
        """
        发现连接设备的服务和特征.
        """
        if self.connected_client and self.connected_client.is_connected:
            services_data = {}
            for service in self.connected_client.services:
                characteristics_data = []
                for char in service.characteristics:
                    characteristics_data.append({
                        "uuid": char.uuid,
                        "properties": char.properties,
                        "handle": char.handle
                    })
                services_data[service.uuid] = characteristics_data
            self.services_resolved_signal.emit(services_data)

    async def read_characteristic(self, char_uuid: str):
        """读取特征值."""
        if self.connected_client and self.connected_client.is_connected:
            try:
                value = await self.connected_client.read_gatt_char(char_uuid)
                self.characteristic_read_signal.emit(char_uuid, value)
                # 发送协议更新信号
                self.protocol_data_updated_signal.emit(char_uuid, value)
            except BleakError as e:
                print(f"读取特征失败 {char_uuid}: {e}")
            except Exception as e:
                print(f"读取时发生未知错误 {char_uuid}: {e}")
                
    async def write_characteristic(self, char_uuid: str, data: bytearray):
        """写入特征值."""
        print(f"BLEManager.write_characteristic: 开始写入数据到 {char_uuid}, 数据={data.hex()}")
        
        if not self.connected_client:
            print("错误: 没有连接的客户端")
            return
            
        if not self.connected_client.is_connected:
            print("错误: 客户端已断开连接")
            return
            
        try:
            # 检查特征是否存在
            try:
                char = self.connected_client.services.get_characteristic(char_uuid)
                if not char:
                    print(f"错误: 特征 {char_uuid} 不存在")
                    return
                print(f"特征 {char_uuid} 找到, 属性: {char.properties}")
            except Exception as e:
                print(f"获取特征时出错: {e}")
                return
                
            # 根据特征属性选择写入方式
            if "write-without-response" in char.properties:
                print("使用 write-without-response 模式写入")
                await self.connected_client.write_gatt_char(char_uuid, data, response=False)
            else:
                print("使用 write-with-response 模式写入")
                await self.connected_client.write_gatt_char(char_uuid, data, response=True)
            print(f"写入成功 {char_uuid}: {data.hex()}")
        except BleakError as e:
            print(f"写入特征失败 {char_uuid}: {str(e)}")
            import traceback
            print(traceback.format_exc())
        except Exception as e:
            print(f"写入时发生未知错误 {char_uuid}: {str(e)}")
            import traceback
            print(traceback.format_exc())
    
    def _notification_callback(self, char_uuid: str, data: bytearray):
        """内部回调，用于将通知数据和UUID一起发出."""
        self.notification_received_signal.emit(char_uuid, data)
        # 发送协议更新信号
        self.protocol_data_updated_signal.emit(char_uuid, data)

    async def start_notifications(self, char_uuid: str):
        """为一个特征开启通知."""
        if self.connected_client and self.connected_client.is_connected:
            try:
                # 使用偏函数将char_uuid传入回调
                handler = lambda _, data: self._notification_callback(char_uuid, data)
                self.notification_handlers[char_uuid] = handler
                await self.connected_client.start_notify(char_uuid, handler)
                print(f"已为 {char_uuid} 开启通知")
            except BleakError as e:
                print(f"为 {char_uuid} 开启通知失败: {e}")
            except Exception as e:
                print(f"开启通知时发生未知错误 {char_uuid}: {e}")

    async def stop_notifications(self, char_uuid: str):
        """为一个特征关闭通知."""
        if self.connected_client and self.connected_client.is_connected:
            try:
                await self.connected_client.stop_notify(char_uuid)
                if char_uuid in self.notification_handlers:
                    del self.notification_handlers[char_uuid]
                print(f"已为 {char_uuid} 关闭通知")
            except BleakError as e:
                print(f"为 {char_uuid} 关闭通知失败: {e}")
            except Exception as e:
                print(f"关闭通知时发生未知错误 {char_uuid}: {e}") 